package br.com.felinelayer.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * Message class
 */
public final class Message {

    /** JSON object */
    private JSONObject jsonObj;

    /**
     * Convert message list to JSON array
     *
     * @param messages
     * @return JSONArray
     */
    private static JSONArray arrayMessageToJSON( List<Message> messages ) {
        JSONArray ja = new JSONArray();
        try {
            for ( Message c : messages ) {
                ja.put( new JSONObject( c.toString() ) );
            }
        } catch ( Exception ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return ja;
    }

    /**
     * Conver message list to a single message batch
     *
     * @param messages
     * @return Message
     */
    public static Message arrayToMessageBatch( List<Message> messages ) {
        return new Message( Protocol.BATCH ).setValueByName( Protocol.BATCH, messages );
    }

    /**
     * Convert a single message to a list message
     *
     * @param message
     * @return List message
     */
    public static List<Message> convertToArray( Message message ) {
        List<Message> messages;
        if ( message.isArray() ) {
            messages = message.getBatchMessagesList();
        } else {
            messages = new ArrayList<>();
            messages.add( message );
        }
        return messages;
    }

    public Message() {
        setFromString( "" );
    }

    public Message( String name ) {
        setFromString( "" ).setName( name );
    }

    public Message setFromString( String fullString ) {
        try {
            if ( fullString.isEmpty() ) {
                jsonObj = new JSONObject();
            } else {
                jsonObj = new JSONObject( fullString );
            }
        } catch ( JSONException ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return this;
    }

    @Override
    public String toString() {
        return jsonObj.toString();
    }

    public String getName() {
        return getValueByName( Protocol.MSG_NAME );
    }

    public Message setName( String name ) {
        setValueByName( Protocol.MSG_NAME, name );
        return this;
    }

    /**
     * Returns names of properties
     *
     * @return Properties names
     */
    public String[] getNames() {
        return JSONObject.getNames( jsonObj );
    }

    public long getLongByName( String name ) {
        return Long.parseLong( getValueByName( name ) );
    }

    public int getIntByName( String name ) {
        return Integer.parseInt( getValueByName( name ) );
    }

    public Object getObjectByName( String name ) {
        try {
            return jsonObj.get( name );
        } catch ( JSONException ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return "";
    }

    public String getValueByName( String name ) {
        try {
            return jsonObj.getString( name );
        } catch ( JSONException ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return "";
    }

    public String[] getValueArray( String name ) {
        try {
            JSONArray ja = jsonObj.getJSONArray( name );
            String[] result = new String[ja.length()];
            for ( int i = 0; i < ja.length(); i++ ) {
                result[i] = ja.getString( i );
            }
            return result;
        } catch ( JSONException ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return null;
    }

    public Message setValueByName( String name, long value ) {
        return setValueByName( name, value + "" );
    }

    public Message setValueByName( String name, int value ) {
        return setValueByName( name, value + "" );
    }

    public Message setObjectByName( String name, Object value ) {
        try {
            jsonObj.put( name, value );
        } catch ( JSONException ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return this;
    }

    public Message setValueByName( String name, String value ) {
        return setObjectByName( name, value );
    }

    public List<Message> getMessageArray( String name ) {
        ArrayList<Message> result = new ArrayList<>();
        try {
            JSONArray ja = jsonObj.getJSONArray( name );
            for ( int i = 0; i < ja.length(); i++ ) {
                result.add( new Message().setFromString( ja.getString( i ) ) );
            }
        } catch ( JSONException ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return result;
    }

    public Message setValueByName( String name, List<Message> messages ) {
        try {
            JSONArray ja = arrayMessageToJSON( messages );
            setObjectByName( name, ja );
        } catch ( Exception ex ) {
            Logger.getLogger( Message.class.getName() ).log( Level.SEVERE, null, ex );
        }
        return this;
    }

    public Message setValueByName( String name, Collection<String> values ) {
        JSONArray ja = new JSONArray( values );
        setObjectByName( name, ja );
        return this;
    }

    public boolean isPropertyExists( String name ) {
        return !jsonObj.isNull( name );
    }

    public boolean isArray() {
        return isPropertyExists( Protocol.BATCH );
    }

    public Message deleteValue( String name ) {
        jsonObj.remove( name );
        return this;
    }

    public List<Message> getBatchMessagesList() {
        return getMessageArray( Protocol.BATCH );
    }

    /**
     * Copy values from other message
     *
     * @param sourceMessage
     * @return Current message
     */
    public Message copyValuesFrom( Message sourceMessage ) {
        for ( String n : sourceMessage.getNames() ) {
            // Ignores name property
            if ( n.equals( Protocol.MSG_NAME ) ) {
                continue;
            }
            // Copy value
            setObjectByName( n, sourceMessage.getObjectByName( n ) );
        }
        return this;
    }

    /**
     * Create new message copying from this current instance
     *
     * @return Message
     */
    public Message cloneMessage() {
        return new Message().setFromString( toString() );
    }

}
